package main

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
)

/*
没错，在很多时候，我们需要创造出错误（即error类型的值）并把它传递给上层程序。这很简单。只需调用标准库代码包errors的New函数即可。例如，我们只要在readFile函数的开始处加入下面这段代码就可以更快的在参数值无效时告知调用方：

if path == "" {
    return nil, errors.New("The parameter is invalid!")
}
    errors.New是一个很常用的函数。在Go语言标准库的代码包中有很多由此函数创建出来的错误值，比如os.ErrPermission、io.EOF等变量的值。我们可以很方便地用操作符==来判断一个error类型的值与这些变量的值是否相等，从而来确定错误的具体类别。就拿io.EOF来说，它代表了一个信号。该信号用于通知数据读取方已无更多数据可读。我们在得到这样一个错误的时候不应该把它看成一个真正的错误，而应该只去结束相应的读取操作。请看下面的示例：

br := bufio.NewReader(file)
var buf bytes.Buffer
for { 
    ba, isPrefix, err := br.ReadLine()
    if err != nil {
        if err == io.EOF {
            break
        }
        fmt.Printf("Error: %s\n", err)
        break
    }
    buf.Write(ba)
    if !isPrefix {
        buf.WriteByte('\n')
    }
}
*/

func innerFunc() {
	fmt.Println("Enter innerFunc")
	defer func() {
		if p := recover(); p != nil {
			fmt.Printf("Fatal error:%s\n", p)
		}

	}()
	panic(errors.New("Occur a panic!"))
	fmt.Println("Quit innerFunc")
}

func outerFunc() {
	fmt.Println("Enter outerFunc")
	innerFunc()
	fmt.Println("Quit outerFunc")
}

func read(r io.Reader) ([]byte, error) {
	br := bufio.NewReader(r)
	var buf bytes.Buffer
	for {
		ba, isPrefix, err := br.ReadLine()
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}
		buf.Write(ba)
		if !isPrefix {
			buf.WriteByte('\n')
		}
	}
	return buf.Bytes(), nil
}

func readFile(path string) ([]byte, error) {
	parentPath, err := os.Getwd()
	if err != nil {
		return nil, err
	}
	fullPath := filepath.Join(parentPath, path)
	file, err := os.Open(fullPath)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	return read(file)
}

func main0() {
	path := "learn0.go"
	ba, err := readFile(path)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
	}
	fmt.Printf("The content of '%s':\n%s\n", path, ba)
}